
/*
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

/*
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.

    To request the provided software under a different license you can contact us at
    support@xoronos.com
*/

#include "xrn_crypto.h"
#include "xrn_common.h"
#include <pthread.h>

int non_linear_start_check( xnum_t num, xrn_matrix_t * xm
 )
{

    int i;
    int neq;
    int error;

    XNUM_CMP( num, xm->xrn_dead_code, neq );

    error = neq == 0;

    for ( i = 0; i < XNBITS; i++ ) {
        XNUM_CMP( num, xm->xvector[i].entry_point, neq );
        error = ( neq == 0 ) || error;
    }

    if ( error == 1 ) {

        return XERR_NLINEAR_START;

    }

    return XSUCCESS;

}

// This function is used to performe the block encryption/decryption algorithm
int xrn_encrypt_symmetric_internal( xrn_matrix_t * xm,
                                    xstate_t * bv,
                                    uint64_t block_index,
                                    xstate_t * hidden_state,
                                    xnum_t init_vector, xstate_t * output_block,
                                    xrn_crypto_extra_settings_t settings
 )
{
    int i;
    xstate_t state;
    int state_size;
    int ret;

    if ( ( xm == NULL ) || ( bv == NULL ) || ( hidden_state == NULL ) || ( output_block == NULL ) ) {
        XPRINT_ERR( 4, XERR_NULL_MSG );
        return XERR_NULL;
    }

    state_size = settings.state_size + 1;

    state.xnums = ( xnum_t * )
        malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( state.xnums == NULL ) {
        XPRINT_ERR( 5, XERR_NULL_MSG );
        return XERR_NULL;
    }

    // generate numbers from the initialization vector
    init_round_state( xm, init_vector, block_index, &state, settings );

    // mix the numbers
    ret = xrn_round_state( &state, xm, hidden_state, &state, settings );
    if ( XSUCCESS != ret ) {
        XRN_FREE_XSTATE( state.xnums, settings.state_size );
        XPRINT_ERR( 6, XERR_GENERAL_MSG );
        return ret;
    }

    // xor mixed numbers with the block with random numbers
    for ( i = 0; i < state_size; i++ ) {
        X_XOR3( bv->xnums[i], state.xnums[i], output_block->xnums[i] );
    }

    XRN_FREE_XSTATE( state.xnums, settings.state_size );
    return XSUCCESS;
}

int loop_split_bytes( uint8_t * in_bytes,
                      uint64_t in_length,
                      uint8_t * out_bytes_challenge,
                      uint64_t out_length_challenge,
                      uint64_t * num_encoded_bytes_challenge,
                      uint8_t * out_bytes_solution,
                      uint64_t out_length_solution,
                      uint64_t * num_encoded_bytes_solution,
                      xrn_encoding_param_t enc_param_challenge, xrn_crypto_extra_settings_t settings
 )
{
    int error;

    uint64_t block_i, i, sizeblock;
    load_variables_t load_variables;
    xstate_t state;
    int state_size;
    int ret, final_ret;
    xrn_encoding_tmp_param_t enc_param_tmp;
    xnum_t num;
    xnum_t num0;
    xnum_t num1;
    xnum_t prevnum;
    int first_read;

    if ( ( in_bytes == NULL ) || ( out_bytes_challenge == NULL ) || ( out_bytes_solution == NULL )
         || ( num_encoded_bytes_challenge == NULL ) || ( num_encoded_bytes_solution == NULL ) ) {
        XPRINT_ERR( 49, XERR_NULL_MSG );
        return XERR_NULL;
    }

    final_ret = XSUCCESS;

    state_size = settings.state_size + 1;
    first_read = 1;

    // allocate state    
    state.xnums = ( xnum_t * ) malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( state.xnums == NULL ) {
        XPRINT_ERR( 50, XERR_NULL_MSG );
        return XERR_NULL;
    }

    // initialize load variables
    crypto_init( &load_variables, settings.crc_bytes );

    // Encrypting blocks
    for ( block_i = 0; !load_variables.last_block; block_i++ ) {

        // get the file
        error =
            bytes_2_cipher( in_bytes, ( int ) in_length, ( int ) block_i, &state, &load_variables, settings );
        if ( error ) {
            XRN_FREE_XSTATE( state.xnums, settings.state_size );
            XPRINT_ERR( 53, XERR_IO_MSG );
            return XERR_IO;
        }

        // encode state
        ret =
            xrn_encode( &enc_param_challenge,
                        ( uint8_t * ) state.xnums,
                        ( uint8_t * ) state.xnums,
                        ( uint64_t ) sizeof( xnum_t ) * ( ( uint64_t ) state_size ), &enc_param_tmp );

        final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
        if ( final_ret == XERR_GENERAL ) {
            XRN_FREE_XSTATE( state.xnums, settings.state_size );
            XPRINT_ERR( 1198, XERR_GENERAL_MSG );
            return ret;
        }

        if ( first_read == 1 ) {

            // compute solution
            num0 = state.xnums[0];
            num1 = state.xnums[1];
            X_XOR3( num0, num1, num );
            num0 = state.xnums[2];
            X_XOR3( num0, num, num );

            if ( ( ( *num_encoded_bytes_solution ) + sizeof( xnum_t ) ) > out_length_solution ) {
                XRN_FREE_XSTATE( state.xnums, settings.state_size );
                XPRINT_ERR( 54, XERR_GENERAL_MSG );
                return XERR_GENERAL;
            }

            // write solution
            for ( i = 0; i < sizeof( num ); i++ ) {

                out_bytes_solution[( *num_encoded_bytes_solution ) + i] = ( ( uint8_t * ) & num )[i];

            }

            *num_encoded_bytes_solution = *num_encoded_bytes_solution + sizeof( num );

            // fold challenge
            for ( i = 0; i < state_size - 1; i++ ) {
                num0 = state.xnums[i];
                num1 = state.xnums[i + 1];
                X_XOR3( num0, num1, num );
                state.xnums[i] = num;
            }

            // get the last number of the state
            prevnum = num1;
            sizeblock = sizeof( xnum_t ) * ( ( uint64_t ) ( state_size - 1 ) );

            if ( ( ( *num_encoded_bytes_challenge ) + sizeblock ) > out_length_challenge ) {
                XRN_FREE_XSTATE( state.xnums, settings.state_size );
                XPRINT_ERR( 1195, XERR_GENERAL_MSG );
                return XERR_GENERAL;
            }

            // write challenge
            for ( i = 0; i < sizeblock; i++ ) {
                out_bytes_challenge[( *num_encoded_bytes_challenge ) + i] =
                    ( ( uint8_t * ) & ( state.xnums[0] ) )[i];
            }

            *num_encoded_bytes_challenge = *num_encoded_bytes_challenge + sizeblock;

        } else {

            // fold challenge
            for ( i = 0; i < state_size; i++ ) {
                num0 = prevnum;
                num1 = state.xnums[i];
                X_XOR3( num0, num1, num );
                state.xnums[i] = num;
                prevnum = num1;
            }

            sizeblock = sizeof( xnum_t ) * ( ( uint64_t ) ( state_size ) );

            if ( ( ( *num_encoded_bytes_challenge ) + sizeblock ) > out_length_challenge ) {
                XRN_FREE_XSTATE( state.xnums, settings.state_size );
                XPRINT_ERR( 1196, XERR_GENERAL_MSG );
                return XERR_GENERAL;
            }

            // write challenge
            for ( i = 0; i < sizeblock; i++ ) {
                out_bytes_challenge[( *num_encoded_bytes_challenge ) + i] =
                    ( ( uint8_t * ) & ( state.xnums[0] ) )[i];
            }

            *num_encoded_bytes_challenge = *num_encoded_bytes_challenge + sizeblock;

        }

        first_read = 0;

    }

    XRN_FREE_XSTATE( state.xnums, settings.state_size );
    return final_ret;

}

int loop_join_stream( FILE * input_file_challenge_fpr,
                      FILE * input_file_solution_fpr,
                      FILE * output_file_join_fpw,
                      xrn_encoding_param_t enc_param_challenge, xrn_crypto_extra_settings_t settings
 )
{
    xrn_encoding_tmp_param_t tmp;
    uint64_t block_i, c_fifo_size;
    uint64_t computed_crc[9], read_crc, read_shuffled_crc, crc_mask;
    uint8_t c, *c_fifo;
    uint8_t crc8;
    uint16_t crc16;
    uint32_t crc32;
    uint64_t crc64;
    xnum_t num0, num1, num2;
    int final_ret, ret;

    int i, l, read_block1, fifo_index, first_loop;

    XBIT_TYPE padding_start_detected, padding_detected, crc_detected;
    xstate_t input_block_variable;
    int state_size;

    if ( ( input_file_challenge_fpr == NULL ) || ( input_file_solution_fpr == NULL )
         || ( output_file_join_fpw == NULL ) ) {
        XPRINT_ERR( 78, XERR_NULL_MSG );
        return XERR_NULL;
    }

    state_size = settings.state_size + 1;
    read_shuffled_crc = 0;
    c_fifo_size = ( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );

    c_fifo = ( uint8_t * ) malloc( ( uint64_t ) sizeof( uint8_t ) * c_fifo_size );
    if ( c_fifo == NULL ) {
        XPRINT_ERR( 79, XERR_NULL_MSG );
        return XERR_NULL;
    }

    input_block_variable.xnums =
        ( xnum_t * ) malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( input_block_variable.xnums == NULL ) {
        XRN_FREEN( c_fifo, c_fifo_size );
        XPRINT_ERR( 82, XERR_NULL_MSG );
        return XERR_NULL;
    }

    for ( l = 0; l < 9; l++ ) {
        computed_crc[l] = 0xffffffffffffffff;
    }

    read_crc = 0x0000000000000000;
    if ( settings.crc_bytes == 1 ) {
        crc_mask = 0x00000000000000ff;
    } else if ( settings.crc_bytes == 2 ) {
        crc_mask = 0x000000000000ffff;
    } else if ( settings.crc_bytes == 4 ) {
        crc_mask = 0x00000000ffffffff;
    } else if ( settings.crc_bytes == 8 ) {
        crc_mask = 0xffffffffffffffff;
    } else {

        XRN_FREEN( c_fifo, c_fifo_size );
        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
        XPRINT_ERR( 83, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    final_ret = XSUCCESS;

    crc_detected = 0;
    padding_start_detected = 0;
    padding_detected = 0;
    fifo_index = 0;
    first_loop = 1;

    state_size = settings.state_size + 1;

    // Decrypting blocks
    for ( block_i = 0; !xrn_feof( input_file_challenge_fpr ); block_i++ ) {

        if ( first_loop == 1 ) {

            // the first group has one less number

            // read challenge
            read_block1 = ( int )
                fread( input_block_variable.xnums,
                       ( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) ( state_size - 1 ) ), 1,
                       input_file_challenge_fpr );

            if ( read_block1 != 1 ) {
                XRN_FREEN( c_fifo, c_fifo_size );
                XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                XPRINT_ERR( 56, XERR_SETTINGS_MSG );
                return XERR_SETTINGS;
            }

            // read solution
            read_block1 = ( int )
                fread( &num2, sizeof( num2 ), 1, input_file_solution_fpr );

            if ( read_block1 != 1 ) {
                XRN_FREEN( c_fifo, c_fifo_size );
                XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                XPRINT_ERR( 1193, XERR_SETTINGS_MSG );
                return XERR_SETTINGS;
            }

            // get the first number
            // num2 = A xor B xor C
            // num1 = B xor C
            num1 = input_block_variable.xnums[1];
            // num0 = A
            X_XOR3( num2, num1, num0 );

            for ( i = 0; i < state_size - 1; i++ ) {
                num1 = input_block_variable.xnums[i];
                X_XOR3( num0, num1, num2 );
                input_block_variable.xnums[i] = num0;
                num0 = num2;
            }

            input_block_variable.xnums[state_size - 1] = num0;

        } else {

            read_block1 = ( int )
                fread( input_block_variable.xnums,
                       ( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size ), 1,
                       input_file_challenge_fpr );

            if ( read_block1 != 1 ) {
                XRN_FREEN( c_fifo, c_fifo_size );
                XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                XPRINT_ERR( 1192, XERR_SETTINGS_MSG );
                return XERR_SETTINGS;
            }

            // num0 is initialized by the previous loop
            for ( i = 0; i < state_size; i++ ) {
                num1 = input_block_variable.xnums[i];
                X_XOR3( num0, num1, num2 );
                input_block_variable.xnums[i] = num2;
                num0 = num2;
            }

        }

        // encode state
        ret =
            xrn_decode( &enc_param_challenge, ( uint8_t * ) input_block_variable.xnums,
                        ( uint8_t * ) input_block_variable.xnums,
                        ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size, &tmp );

        final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
        if ( final_ret == XERR_GENERAL ) {
            XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
            XPRINT_ERR( 1191, XERR_IO_MSG );
            return ret;
        }

        // Calculate crc
        for ( i = 0; i < state_size * ( int ) sizeof( xnum_t ); i++ ) {

            c = ( ( uint8_t * ) input_block_variable.xnums )[i];

            // fifo of calculated crc
            for ( l = settings.crc_bytes - 1; l >= 0; l-- ) {
                computed_crc[l + 1] = computed_crc[l];
            }

            if ( settings.crc_bytes == 1 ) {

                crc8 = ( uint8_t ) ( computed_crc[0] & crc_mask );

                xrn_crc8( &c, 1, &crc8 );
                computed_crc[0] = ( uint64_t ) crc8 & crc_mask;

                read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );

                read_shuffled_crc = read_crc;
            } else if ( settings.crc_bytes == 2 ) {

                crc16 = ( uint16_t ) ( computed_crc[0] & crc_mask );
                xrn_crc16( &c, 1, &crc16 );
                computed_crc[0] = ( uint64_t ) crc16 & crc_mask;

                read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );

                read_shuffled_crc =
                    ( ( read_crc << 8 ) & 0x000000000000ff00 ) | ( ( read_crc >> 8 ) & 0x00000000000000ff );
            } else if ( settings.crc_bytes == 4 ) {

                crc32 = ( uint32_t ) ( computed_crc[0] & crc_mask );
                xrn_crc32( &c, 1, &crc32 );
                computed_crc[0] = ( uint64_t ) crc32 & crc_mask;

                read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );

                read_shuffled_crc =
                    ( ( read_crc << 24 ) & 0x00000000ff000000 ) |
                    ( ( read_crc << 8 ) & 0x0000000000ff0000 ) |
                    ( ( read_crc >> 8 ) & 0x000000000000ff00 ) | ( ( read_crc >> 24 ) & 0x00000000000000ff );

            } else if ( settings.crc_bytes == 8 ) {

                crc64 = ( uint64_t ) ( computed_crc[0] & crc_mask );
                xrn_crc64( &c, 1, &crc64 );
                computed_crc[0] = ( uint64_t ) crc64 & crc_mask;

                read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );

                read_shuffled_crc =
                    ( ( read_crc << 56 ) & 0xff00000000000000 ) |
                    ( ( read_crc << 40 ) & 0x00ff000000000000 ) |
                    ( ( read_crc << 24 ) & 0x0000ff0000000000 ) |
                    ( ( read_crc << 8 ) & 0x000000ff00000000 ) |
                    ( ( read_crc >> 8 ) & 0x00000000ff000000 ) |
                    ( ( read_crc >> 24 ) & 0x0000000000ff0000 ) |
                    ( ( read_crc >> 40 ) & 0x000000000000ff00 ) | ( ( read_crc >> 56 ) & 0x00000000000000ff );
            }

            c_fifo[fifo_index] = c;
            fifo_index++;
            if ( padding_detected ) {
                padding_detected = c == 0x00;
                crc_detected = 0;
                padding_start_detected = 0;
            }

            if ( padding_start_detected ) {
                padding_detected = c == 0x00;
                crc_detected = 0;
                padding_start_detected = 0;
            }

            if ( crc_detected ) {
                crc_detected = 0;
                padding_start_detected = c == 0x80;
            }

            if ( ( computed_crc[settings.crc_bytes] & crc_mask ) == ( read_shuffled_crc & crc_mask ) ) {
                crc_detected = 1;
            }

            if ( ( crc_detected ) && ( !( padding_start_detected || padding_detected ) ) ) {

                // restore the fifo except the character in the crc
                if ( fwrite
                     ( c_fifo, sizeof( uint8_t ),
                       ( uint64_t ) fifo_index - ( uint64_t ) settings.crc_bytes,
                       output_file_join_fpw ) !=
                     ( ( uint64_t ) fifo_index - ( uint64_t ) settings.crc_bytes ) ) {

                    XRN_FREEN( c_fifo, c_fifo_size );
                    XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

                    XPRINT_ERR( 86, XERR_IO_MSG );
                    return XERR_IO;
                }

                // restore in the fifo the partial crc
                for ( l = 0; l < settings.crc_bytes; l++ ) {
                    c_fifo[l] = c_fifo[fifo_index - settings.crc_bytes + l];
                }

                fifo_index = settings.crc_bytes;
            } else if ( ( fifo_index > settings.crc_bytes )
                        && ( !( padding_start_detected || padding_detected ) ) ) {

                // restore the fifo except the character in the crc
                if ( fwrite
                     ( c_fifo, sizeof( uint8_t ),
                       ( uint64_t ) fifo_index - ( uint64_t ) settings.crc_bytes,
                       output_file_join_fpw ) !=
                     ( ( uint64_t ) fifo_index - ( uint64_t ) settings.crc_bytes ) ) {

                    XRN_FREEN( c_fifo, c_fifo_size );
                    XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

                    XPRINT_ERR( 87, XERR_IO_MSG );
                    return XERR_IO;
                }

                // restore in the fifo the partial crc
                for ( l = 0; l < settings.crc_bytes; l++ ) {
                    c_fifo[l] = c_fifo[fifo_index - settings.crc_bytes + l];
                }

                fifo_index = settings.crc_bytes;
            }

        }

        first_loop = 0;

    }

    if ( !( padding_start_detected || padding_detected ) ) {

        XRN_FREEN( c_fifo, c_fifo_size );
        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
        XPRINT_ERR( 88, XERR_END_DEC_MSG );
        return XERR_END_DEC;
    }

    XRN_FREEN( c_fifo, c_fifo_size );
    XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

    return final_ret;
}

int loop_split_stream( FILE * input_file_fpr,
                       FILE * output_file_challenge_fpw,
                       FILE * output_file_solution_fpw,
                       xrn_encoding_param_t enc_param_challenge, xrn_crypto_extra_settings_t settings
 )
{
    int final_ret, ret, error;
    int first_read, i;

    uint64_t block_i;
    load_variables_t load_variables;
    xstate_t state;
    xnum_t num;
    xnum_t num0;
    xnum_t num1;
    xnum_t prevnum;
    int state_size;
    xrn_encoding_tmp_param_t tmp;

    if ( ( input_file_fpr == NULL ) || ( output_file_solution_fpw == NULL )
         || ( output_file_challenge_fpw == NULL ) ) {
        XPRINT_ERR( 98, XERR_NULL_MSG );
        return XERR_NULL;
    }

    final_ret = XSUCCESS;
    state_size = settings.state_size + 1;

    // allocate state    
    state.xnums = ( xnum_t * ) malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( state.xnums == NULL ) {
        XPRINT_ERR( 99, XERR_NULL_MSG );
        return XERR_NULL;
    }

    first_read = 1;

    // initialize load variables
    crypto_init( &load_variables, settings.crc_bytes );

    // Encrypting blocks
    for ( block_i = 0; !load_variables.last_block; block_i++ ) {

        // get the file
        error = plain_text_2_cipher( input_file_fpr, ( int ) block_i, &state, &load_variables, settings );
        if ( error ) {
            XRN_FREE_XSTATE( state.xnums, settings.state_size );
            XPRINT_ERR( 102, XERR_IO_MSG );
            return XERR_IO;
        }

        // encode state
        ret =
            xrn_encode( &enc_param_challenge, ( uint8_t * ) state.xnums, ( uint8_t * ) state.xnums,
                        ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size, &tmp );

        final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
        if ( final_ret == XERR_GENERAL ) {
            XRN_FREE_XSTATE( state.xnums, settings.state_size );
            XPRINT_ERR( 1189, XERR_IO_MSG );
            return ret;
        }

        // write solution
        if ( first_read == 1 ) {

            num0 = state.xnums[0];
            num1 = state.xnums[1];
            X_XOR3( num0, num1, num );
            num0 = state.xnums[2];
            X_XOR3( num0, num, num );
            if ( 1 != fwrite( &num, sizeof( num ), 1, output_file_solution_fpw ) ) {
                XRN_FREE_XSTATE( state.xnums, settings.state_size );
                XPRINT_ERR( 1197, XERR_IO_MSG );
                return XERR_IO;
            }

            // fold challenge
            for ( i = 0; i < state_size - 1; i++ ) {
                num0 = state.xnums[i];
                num1 = state.xnums[i + 1];
                X_XOR3( num0, num1, num );
                state.xnums[i] = num;
            }

            // get the last number of the state
            prevnum = num1;

            // write challenge
            if ( 1 !=
                 fwrite( state.xnums, ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) ( state_size - 1 ), 1,
                         output_file_challenge_fpw ) ) {
                XRN_FREE_XSTATE( state.xnums, settings.state_size );
                XPRINT_ERR( 57, XERR_IO_MSG );
                return XERR_IO;
            }

        } else {

            // fold challenge
            for ( i = 0; i < state_size; i++ ) {
                num0 = prevnum;
                num1 = state.xnums[i];
                X_XOR3( num0, num1, num );
                state.xnums[i] = num;
                prevnum = num1;
            }

            if ( 1 !=
                 fwrite( state.xnums, ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size, 1,
                         output_file_challenge_fpw ) ) {
                XRN_FREE_XSTATE( state.xnums, settings.state_size );
                XPRINT_ERR( 1190, XERR_IO_MSG );
                return XERR_IO;
            }

        }

        first_read = 0;

    }

    XRN_FREE_XSTATE( state.xnums, settings.state_size );

    return final_ret;

}

int check_crypto_extra_settings_value( xrn_crypto_extra_settings_t settings
 )
{

    if ( ( settings.crc_bytes != 1 ) && ( settings.crc_bytes != 2 )
         && ( settings.crc_bytes != 4 ) && ( settings.crc_bytes != 8 ) ) {
        XPRINT_ERR( 109, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    if ( ( settings.state_size < XRN_MIN_ALLOWED_STATE_SIZE )
         && ( settings.state_size > ( sizeof( xnum_t ) * 8 ) - 1 ) ) {
        XPRINT_ERR( 110, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    if ( ( settings.thread_enable != 0 )
         && ( settings.thread_enable != 1 ) ) {
        XPRINT_ERR( 111, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    if ( ( settings.const_exec_time != 0 )
         && ( settings.const_exec_time != 1 ) ) {
        XPRINT_ERR( 113, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    if ( XSUCCESS != xrn_check_encoding_interface( settings.encoding_settings ) ) {
        XPRINT_ERR( 114, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    return XSUCCESS;
}

int loop_enc_bytes( uint8_t * in_bytes,
                    uint64_t in_length,
                    uint8_t * out_bytes,
                    uint64_t out_length,
                    uint64_t out_index,
                    uint64_t * num_encoded_bytes,
                    xstate_t * hidden_state,
                    xrn_matrix_t * xm, xnum_t init_vector, xrn_encoding_param_t enc_param,
                    xrn_crypto_extra_settings_t settings
 )
{
    uint64_t block_i, out_index_tmp;
    int error, i, l;
    int state_size;
    xstate_t obv;
    xstate_t ibv;
    int ret, final_ret;
    xrn_encoding_tmp_param_t enc_param_tmp;

    state_size = settings.state_size + 1;
    load_variables_t load_variables;

    if ( ( in_bytes == NULL ) || ( out_bytes == NULL ) || ( num_encoded_bytes == NULL )
         || ( hidden_state == NULL ) || ( xm == NULL ) ) {
        XPRINT_ERR( 156, XERR_NULL_MSG );
        return XERR_NULL;
    }

    final_ret = XSUCCESS;

    crypto_init( &load_variables, settings.crc_bytes );
    obv.xnums = ( xnum_t * )
        malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( obv.xnums == NULL ) {
        XPRINT_ERR( 157, XERR_NULL_MSG );
        return XERR_NULL;
    }

    ibv.xnums = ( xnum_t * )
        malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( ibv.xnums == NULL ) {
        XRN_FREE_XSTATE( obv.xnums, settings.state_size );
        XPRINT_ERR( 158, XERR_NULL_MSG );
        return XERR_NULL;
    }

    // Encrypting blocks
    for ( block_i = 0; !load_variables.last_block; block_i++ ) {

        error =
            bytes_2_cipher( in_bytes, ( int ) in_length, ( int ) block_i, &ibv, &load_variables, settings );
        if ( error ) {
            XRN_FREE_XSTATE( ibv.xnums, settings.state_size );
            XRN_FREE_XSTATE( obv.xnums, settings.state_size );
            XPRINT_ERR( 159, XERR_IO_MSG );
            return XERR_IO;
        }

        // Perform encryption
        ret = xrn_encrypt_symmetric_internal( xm, &ibv, block_i, hidden_state, init_vector, &obv, settings );
        if ( XSUCCESS != ret ) {
            XRN_FREE_XSTATE( ibv.xnums, settings.state_size );
            XRN_FREE_XSTATE( obv.xnums, settings.state_size );
            XPRINT_ERR( 160, XERR_GENERAL_MSG );
            return ret;
        }

        if ( ( int ) out_index + ( int ) state_size * ( int ) sizeof( xnum_t ) > out_length ) {
            XRN_FREE_XSTATE( ibv.xnums, settings.state_size );
            XRN_FREE_XSTATE( obv.xnums, settings.state_size );
            XPRINT_ERR( 161, XERR_BUFF_MSG );
            return XERR_BUFF;
        }

        // Storing block
        for ( i = 0; i < state_size; i++ ) {
            for ( l = 0; l < sizeof( xnum_t ); l++ ) {
                out_bytes[out_index] = ( ( uint8_t * ) & obv.xnums[i] )[l];
                out_index++;
            }

            // encoding output bytes

            out_index_tmp = out_index - sizeof( xnum_t );
            ret =
                xrn_encode( &enc_param, &out_bytes[out_index_tmp], &out_bytes[out_index_tmp],
                            sizeof( xnum_t ), &enc_param_tmp );
            final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
            if ( final_ret == XERR_GENERAL ) {
                XRN_FREE_XSTATE( ibv.xnums, settings.state_size );
                XRN_FREE_XSTATE( obv.xnums, settings.state_size );
                XPRINT_ERR( 162, XERR_GENERAL_MSG );
                return ret;
            }

        }

    }

    *num_encoded_bytes = out_index;

    XRN_FREE_XSTATE( ibv.xnums, settings.state_size );
    XRN_FREE_XSTATE( obv.xnums, settings.state_size );

    return final_ret;
}

// This function is used to 
// 1 ) loop through blocks of unencrypted file
// 2 ) perform stream encryption
// 3 ) store the encrypted file
int loop_enc_stream( FILE * fpi,
                     FILE * fpo,
                     xstate_t * hidden_state,
                     xrn_matrix_t * xm, xnum_t init_vector, xrn_encoding_param_t enc_param,
                     xrn_crypto_extra_settings_t settings
 )
{
    uint64_t block_i;
    int error, i;
    int state_size;
    xstate_t obv;
    xstate_t ibv;
    load_variables_t load_variables;
    int ret;

    if ( ( fpi == NULL ) || ( fpo == NULL ) || ( hidden_state == NULL ) || ( xm == NULL ) ) {
        XPRINT_ERR( 163, XERR_NULL_MSG );
        return XERR_NULL;
    }

    state_size = settings.state_size + 1;

    // initialize block variables for load_block_cipher
    crypto_init( &load_variables, settings.crc_bytes );
    obv.xnums = ( xnum_t * )
        malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( obv.xnums == NULL ) {
        XPRINT_ERR( 164, XERR_NULL_MSG );
        return XERR_NULL;
    }

    ibv.xnums = ( xnum_t * )
        malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( ibv.xnums == NULL ) {
        XRN_FREE_XSTATE( obv.xnums, settings.state_size );
        XPRINT_ERR( 165, XERR_NULL_MSG );
        return XERR_NULL;
    }

    // Encrypting blocks
    for ( block_i = 0; !load_variables.last_block; block_i++ ) {

        error = plain_text_2_cipher( fpi, ( int ) block_i, &ibv, &load_variables, settings );
        if ( error ) {
            XRN_FREE_XSTATE( ibv.xnums, settings.state_size );
            XRN_FREE_XSTATE( obv.xnums, settings.state_size );
            XPRINT_ERR( 166, XERR_IO_MSG );
            return XERR_IO;
        }

        // perform encryption
        ret = xrn_encrypt_symmetric_internal( xm, &ibv, block_i, hidden_state, init_vector, &obv, settings );
        if ( XSUCCESS != ret ) {
            XRN_FREE_XSTATE( ibv.xnums, settings.state_size );
            XRN_FREE_XSTATE( obv.xnums, settings.state_size );
            XPRINT_ERR( 167, XERR_GENERAL_MSG );
            return ret;
        }

        // Storing block
        for ( i = 0; i < state_size; i++ ) {
            if ( xrn_fwrite_encoder( &obv.xnums[i], sizeof( xnum_t ), 1, fpo, &enc_param ) != 1 ) {
                XRN_FREE_XSTATE( ibv.xnums, settings.state_size );
                XRN_FREE_XSTATE( obv.xnums, settings.state_size );
                XPRINT_ERR( 168, XERR_IO_MSG );
                return XERR_IO;
            }

        }

    }

    XRN_FREE_XSTATE( ibv.xnums, settings.state_size );
    XRN_FREE_XSTATE( obv.xnums, settings.state_size );
    return XSUCCESS;
}

// This function is used to 
// 1 ) loop through blocks of encrypted bytes
// 2 ) perform stream encryption
// 3 ) store the decrypted bytes
int loop_dec_bytes( uint8_t * in_bytes,
                    uint64_t in_length,
                    uint8_t * out_bytes,
                    uint64_t out_length,
                    uint64_t in_index,
                    uint64_t * num_decoded_bytes,
                    xstate_t * hidden_state,
                    xrn_matrix_t * xm, xnum_t init_vector, xrn_encoding_param_t enc_param,
                    xrn_crypto_extra_settings_t settings
 )
{

    uint64_t block_i, c_fifo_size;
    uint64_t computed_crc[9], read_crc, read_shuffled_crc, crc_mask;
    int out_index, l;
    uint8_t c, *c_fifo;
    uint8_t crc8;
    uint16_t crc16;
    uint32_t crc32;
    uint64_t crc64;
    xstate_t output_block_variable;
    int i, read_block, fifo_index;
    XBIT_TYPE padding_start_detected, padding_detected, crc_detected;
    xstate_t input_block_variable;
    int state_size;
    int ret, final_ret;
    xrn_encoding_tmp_param_t enc_param_tmp;

    if ( ( in_bytes == NULL ) || ( out_bytes == NULL ) || ( num_decoded_bytes == NULL )
         || ( hidden_state == NULL ) || ( xm == NULL ) ) {
        XPRINT_ERR( 169, XERR_NULL_MSG );
        return XERR_NULL;
    }

    final_ret = XSUCCESS;
    read_shuffled_crc = 0;
    state_size = settings.state_size + 1;
    c_fifo_size = ( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    out_index = 0;

    c_fifo = ( uint8_t * ) malloc( ( uint64_t ) sizeof( uint8_t ) * c_fifo_size );
    if ( c_fifo == NULL ) {
        XPRINT_ERR( 170, XERR_NULL_MSG );
        return XERR_NULL;
    }

    output_block_variable.xnums = ( xnum_t * )
        malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( output_block_variable.xnums == NULL ) {
        XRN_FREEN( c_fifo, c_fifo_size );
        XPRINT_ERR( 171, XERR_NULL_MSG );
        return XERR_NULL;
    }

    input_block_variable.xnums = ( xnum_t * )
        malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( input_block_variable.xnums == NULL ) {

        XRN_FREE_XSTATE( output_block_variable.xnums, settings.state_size );
        XRN_FREEN( c_fifo, c_fifo_size );

        XPRINT_ERR( 172, XERR_NULL_MSG );
        return XERR_NULL;
    }

    for ( l = 0; l < 9; l++ ) {
        computed_crc[l] = 0xffffffffffffffff;
    }

    read_crc = 0x0000000000000000;
    if ( settings.crc_bytes == 1 ) {
        crc_mask = 0x00000000000000ff;
    } else if ( settings.crc_bytes == 2 ) {
        crc_mask = 0x000000000000ffff;
    } else if ( settings.crc_bytes == 4 ) {
        crc_mask = 0x00000000ffffffff;
    } else if ( settings.crc_bytes == 8 ) {
        crc_mask = 0xffffffffffffffff;
    } else {

        XRN_FREE_XSTATE( output_block_variable.xnums, settings.state_size );
        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
        XRN_FREEN( c_fifo, c_fifo_size );

        XPRINT_ERR( 173, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    crc_detected = 0;
    padding_start_detected = 0;
    padding_detected = 0;
    fifo_index = 0;
    read_block = 1;

    // Decrypting blocks
    for ( block_i = 0; read_block == 1; block_i++ ) {

        if ( ( int ) in_index + ( int ) sizeof( xnum_t ) * state_size > in_length ) {
            read_block = 0;

        } else {

            for ( l = 0; l < ( ( int ) sizeof( xnum_t ) * state_size ); l++ ) {
                ( ( uint8_t * ) & input_block_variable.xnums->xnum )[l] = in_bytes[in_index];
                in_index++;
            }

            // decode bytes

            ret =
                xrn_decode( &enc_param, ( uint8_t * ) & input_block_variable.xnums->xnum,
                            ( uint8_t * ) & input_block_variable.xnums->xnum,
                            ( uint64_t ) ( sizeof( xnum_t ) * ( uint64_t ) state_size ), &enc_param_tmp );

            final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
            if ( final_ret == XERR_GENERAL ) {
                XRN_FREE_XSTATE( output_block_variable.xnums, settings.state_size );
                XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                XRN_FREEN( c_fifo, c_fifo_size );
                XPRINT_ERR( 174, XERR_VERSION_MSG );
                return XERR_VERSION;
            }

        }

        if ( ( read_block >= 2 ) || ( read_block < 0 ) ) {

            XRN_FREE_XSTATE( output_block_variable.xnums, settings.state_size );
            XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
            XRN_FREEN( c_fifo, c_fifo_size );

            XPRINT_ERR( 175, XERR_IO_MSG );
            return XERR_IO;
        }

        // Reading the blocks
        if ( read_block == 1 ) {

            // perform decryption
            ret =
                xrn_encrypt_symmetric_internal( xm, &input_block_variable, block_i, hidden_state, init_vector,
                                                &output_block_variable, settings );

            if ( XSUCCESS != ret ) {

                XRN_FREE_XSTATE( output_block_variable.xnums, settings.state_size );
                XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                XRN_FREEN( c_fifo, c_fifo_size );

                XPRINT_ERR( 176, XERR_GENERAL_MSG );
                return ret;
            }

            // Calculate crc
            for ( i = 0; i < state_size * ( int ) sizeof( xnum_t ); i++ ) {
                c = ( ( uint8_t * )
                      output_block_variable.xnums )[i];

                // fifo of calculated crc
                for ( l = settings.crc_bytes - 1; l >= 0; l-- ) {
                    computed_crc[l + 1] = computed_crc[l];
                }

                if ( settings.crc_bytes == 1 ) {

                    crc8 = ( uint8_t ) ( computed_crc[0] & crc_mask );
                    xrn_crc8( &c, 1, &crc8 );
                    computed_crc[0] = ( uint64_t ) crc8 & crc_mask;

                    read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );
                    read_shuffled_crc = read_crc;
                } else if ( settings.crc_bytes == 2 ) {

                    crc16 = ( uint16_t ) ( computed_crc[0] & crc_mask );
                    xrn_crc16( &c, 1, &crc16 );
                    computed_crc[0] = ( uint64_t ) crc16 & crc_mask;

                    read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );
                    read_shuffled_crc =
                        ( ( read_crc << 8 ) & 0x000000000000ff00 ) |
                        ( ( read_crc >> 8 ) & 0x00000000000000ff );
                } else if ( settings.crc_bytes == 4 ) {

                    crc32 = ( uint32_t ) ( computed_crc[0] & crc_mask );
                    xrn_crc32( &c, 1, &crc32 );
                    computed_crc[0] = ( uint64_t ) crc32 & crc_mask;

                    read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );
                    read_shuffled_crc =
                        ( ( read_crc << 24 ) & 0x00000000ff000000 ) |
                        ( ( read_crc << 8 ) & 0x0000000000ff0000 ) |
                        ( ( read_crc >> 8 ) & 0x000000000000ff00 ) |
                        ( ( read_crc >> 24 ) & 0x00000000000000ff );

                } else if ( settings.crc_bytes == 8 ) {
                    crc64 = ( uint64_t ) ( computed_crc[0] & crc_mask );
                    xrn_crc64( &c, 1, &crc64 );
                    computed_crc[0] = ( uint64_t ) crc64 & crc_mask;

                    read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );
                    read_shuffled_crc =
                        ( ( read_crc << 56 ) & 0xff00000000000000 ) |
                        ( ( read_crc << 40 ) & 0x00ff000000000000 ) |
                        ( ( read_crc << 24 ) & 0x0000ff0000000000 ) |
                        ( ( read_crc << 8 ) & 0x000000ff00000000 ) |
                        ( ( read_crc >> 8 ) & 0x00000000ff000000 ) |
                        ( ( read_crc >> 24 ) & 0x0000000000ff0000 ) |
                        ( ( read_crc >> 40 ) & 0x000000000000ff00 ) |
                        ( ( read_crc >> 56 ) & 0x00000000000000ff );
                }

                c_fifo[fifo_index] = c;
                fifo_index++;
                if ( padding_detected ) {
                    padding_detected = c == 0x00;
                    crc_detected = 0;
                    padding_start_detected = 0;
                }

                if ( padding_start_detected ) {
                    padding_detected = c == 0x00;
                    crc_detected = 0;
                    padding_start_detected = 0;
                }

                if ( crc_detected ) {
                    crc_detected = 0;
                    padding_start_detected = c == 0x80;
                }

                if ( ( computed_crc[settings.crc_bytes] & crc_mask )
                     == ( read_shuffled_crc & crc_mask ) ) {
                    crc_detected = 1;
                }

                if ( ( crc_detected )
                     && ( !( padding_start_detected || padding_detected ) ) ) {

                    if ( out_index + fifo_index - settings.crc_bytes > out_length ) {

                        XRN_FREE_XSTATE( output_block_variable.xnums, settings.state_size );
                        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                        XRN_FREEN( c_fifo, c_fifo_size );

                        XPRINT_ERR( 177, XERR_BUFF_MSG );
                        return XERR_BUFF;
                    }

                    for ( l = 0; l < ( fifo_index - settings.crc_bytes ); l++ ) {
                        out_bytes[out_index] = c_fifo[l];
                        out_index++;
                    }

                    // restore in the fifo the partial crc
                    for ( l = 0; l < settings.crc_bytes; l++ ) {
                        c_fifo[l] = c_fifo[fifo_index - settings.crc_bytes + l];
                    }

                    fifo_index = settings.crc_bytes;
                } else if ( ( fifo_index > settings.crc_bytes )
                            && ( !( padding_start_detected || padding_detected ) ) ) {

                    if ( out_index + fifo_index - settings.crc_bytes > out_length ) {

                        XRN_FREE_XSTATE( output_block_variable.xnums, settings.state_size );
                        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                        XRN_FREEN( c_fifo, c_fifo_size );

                        XPRINT_ERR( 178, XERR_BUFF_MSG );
                        return XERR_BUFF;
                    }

                    // flush fifo except the characters in the crc
                    for ( l = 0; l < ( fifo_index - settings.crc_bytes ); l++ ) {
                        out_bytes[out_index] = c_fifo[l];
                        out_index++;
                    }

                    // restore in the fifo the partial crc
                    for ( l = 0; l < settings.crc_bytes; l++ ) {
                        c_fifo[l] = c_fifo[fifo_index - settings.crc_bytes + l];
                    }

                    fifo_index = settings.crc_bytes;
                }

            }

        }

    }

    if ( !( padding_start_detected || padding_detected ) ) {
        XRN_FREE_XSTATE( output_block_variable.xnums, settings.state_size );
        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
        XRN_FREEN( c_fifo, c_fifo_size );
        XPRINT_ERR( 179, XERR_END_DEC_MSG );
        return XERR_END_DEC;
    }

    *num_decoded_bytes = ( uint64_t ) out_index;

    XRN_FREE_XSTATE( output_block_variable.xnums, settings.state_size );
    XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
    XRN_FREEN( c_fifo, c_fifo_size );

    return final_ret;
}

// This function is used to 
// 1 ) loop through blocks of encrypted file
// 2 ) perform stream encryption
// 3 ) store the decrypted file

int loop_dec_stream( FILE * fpi,
                     FILE * fpo,
                     xstate_t * hidden_state,
                     xrn_matrix_t * xm, xnum_t init_vector, xrn_encoding_param_t enc_param,
                     xrn_crypto_extra_settings_t settings
 )
{

    uint64_t block_i, c_fifo_size;
    uint64_t computed_crc[9], read_crc, read_shuffled_crc, crc_mask;
    uint8_t c, *c_fifo;
    uint8_t crc8;
    uint16_t crc16;
    uint32_t crc32;
    uint64_t crc64;
    xstate_t *output_block_variable;
    int i, l, read_block, fifo_index;
    XBIT_TYPE padding_start_detected, padding_detected, crc_detected;
    xstate_t input_block_variable;
    int state_size;
    int ret, final_ret;

    if ( ( fpi == NULL ) || ( fpo == NULL ) || ( hidden_state == NULL ) || ( xm == NULL ) ) {
        XPRINT_ERR( 180, XERR_NULL_MSG );
        return XERR_NULL;
    }

    final_ret = XSUCCESS;
    read_shuffled_crc = 0;

    state_size = settings.state_size + 1;
    c_fifo_size = ( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    c_fifo = ( uint8_t * ) malloc( ( uint64_t ) sizeof( uint8_t ) * c_fifo_size );
    if ( c_fifo == NULL ) {
        XPRINT_ERR( 181, XERR_NULL_MSG );
        return XERR_NULL;
    }

    output_block_variable = ( xstate_t * )
        malloc( ( uint64_t ) sizeof( xstate_t ) );
    if ( output_block_variable == NULL ) {
        XRN_FREEN( c_fifo, c_fifo_size );
        XPRINT_ERR( 182, XERR_NULL_MSG );
        return XERR_NULL;
    }

    output_block_variable->xnums = ( xnum_t * )
        malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( output_block_variable->xnums == NULL ) {
        XRN_FREEN( c_fifo, c_fifo_size );
        XRN_FREE( output_block_variable );
        XPRINT_ERR( 183, XERR_NULL_MSG );
        return XERR_NULL;
    }

    input_block_variable.xnums = ( xnum_t * )
        malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( input_block_variable.xnums == NULL ) {

        XRN_FREE_XSTATE( output_block_variable->xnums, settings.state_size );
        XRN_FREEN( c_fifo, c_fifo_size );
        XRN_FREE( output_block_variable );

        XPRINT_ERR( 184, XERR_NULL_MSG );
        return XERR_NULL;
    }

    for ( l = 0; l < 9; l++ ) {
        computed_crc[l] = 0xffffffffffffffff;
    }

    read_crc = 0x0000000000000000;
    if ( settings.crc_bytes == 1 ) {
        crc_mask = 0x00000000000000ff;
    } else if ( settings.crc_bytes == 2 ) {
        crc_mask = 0x000000000000ffff;
    } else if ( settings.crc_bytes == 4 ) {
        crc_mask = 0x00000000ffffffff;
    } else if ( settings.crc_bytes == 8 ) {
        crc_mask = 0xffffffffffffffff;
    } else {

        XRN_FREE_XSTATE( output_block_variable->xnums, settings.state_size );
        XRN_FREEN( c_fifo, c_fifo_size );
        XRN_FREE( output_block_variable );
        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

        XPRINT_ERR( 185, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    crc_detected = 0;
    padding_start_detected = 0;
    padding_detected = 0;
    fifo_index = 0;
    read_block = 1;

    // Decrypting blocks
    for ( block_i = 0; read_block == 1; block_i++ ) {

        read_block = ( int )
            xrn_fread_decoder( &input_block_variable.xnums->xnum, sizeof( xnum_t ) * ( uint64_t ) state_size,
                               1, fpi, &enc_param );

        if ( ( read_block >= 2 )
             || ( read_block < 0 ) ) {
            XRN_FREE_XSTATE( output_block_variable->xnums, settings.state_size );
            XRN_FREEN( c_fifo, c_fifo_size );
            XRN_FREE( output_block_variable );
            XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

            XPRINT_ERR( 186, XERR_IO_MSG );
            return XERR_IO;
        }

        // Reading the blocks
        if ( read_block == 1 ) {
            ret =
                xrn_encrypt_symmetric_internal( xm, &input_block_variable, block_i, hidden_state, init_vector,
                                                output_block_variable, settings );

            if ( XSUCCESS != ret ) {

                XRN_FREE_XSTATE( output_block_variable->xnums, settings.state_size );
                XRN_FREEN( c_fifo, c_fifo_size );
                XRN_FREE( output_block_variable );
                XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

                XPRINT_ERR( 187, XERR_GENERAL_MSG );
                return ret;
            }

            // Calculate crc
            for ( i = 0; i < state_size * ( int ) sizeof( xnum_t ); i++ ) {
                c = ( ( uint8_t * )
                      output_block_variable->xnums )[i];

                // fifo of calculated crc
                for ( l = settings.crc_bytes - 1; l >= 0; l-- ) {
                    computed_crc[l + 1] = computed_crc[l];
                }

                if ( settings.crc_bytes == 1 ) {

                    crc8 = ( uint8_t ) ( computed_crc[0] & crc_mask );
                    xrn_crc8( &c, 1, &crc8 );
                    computed_crc[0] = ( uint64_t ) crc8 & crc_mask;

                    read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );
                    read_shuffled_crc = read_crc;
                } else if ( settings.crc_bytes == 2 ) {

                    crc16 = ( uint16_t ) ( computed_crc[0] & crc_mask );
                    xrn_crc16( &c, 1, &crc16 );
                    computed_crc[0] = ( uint64_t ) crc16 & crc_mask;

                    read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );
                    read_shuffled_crc =
                        ( ( read_crc << 8 ) & 0x000000000000ff00 ) |
                        ( ( read_crc >> 8 ) & 0x00000000000000ff );
                } else if ( settings.crc_bytes == 4 ) {

                    crc32 = ( uint32_t ) ( computed_crc[0] & crc_mask );
                    xrn_crc32( &c, 1, &crc32 );
                    computed_crc[0] = ( uint64_t ) crc32 & crc_mask;

                    read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );
                    read_shuffled_crc =
                        ( ( read_crc << 24 ) & 0x00000000ff000000 ) |
                        ( ( read_crc << 8 ) & 0x0000000000ff0000 ) |
                        ( ( read_crc >> 8 ) & 0x000000000000ff00 ) |
                        ( ( read_crc >> 24 ) & 0x00000000000000ff );

                } else if ( settings.crc_bytes == 8 ) {

                    crc64 = ( uint64_t ) ( computed_crc[0] & crc_mask );
                    xrn_crc64( &c, 1, &crc64 );
                    computed_crc[0] = ( uint64_t ) crc64 & crc_mask;

                    read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );
                    read_shuffled_crc =
                        ( ( read_crc << 56 ) & 0xff00000000000000 ) |
                        ( ( read_crc << 40 ) & 0x00ff000000000000 ) |
                        ( ( read_crc << 24 ) & 0x0000ff0000000000 ) |
                        ( ( read_crc << 8 ) & 0x000000ff00000000 ) |
                        ( ( read_crc >> 8 ) & 0x00000000ff000000 ) |
                        ( ( read_crc >> 24 ) & 0x0000000000ff0000 ) |
                        ( ( read_crc >> 40 ) & 0x000000000000ff00 ) |
                        ( ( read_crc >> 56 ) & 0x00000000000000ff );
                }

                c_fifo[fifo_index] = c;
                fifo_index++;
                if ( padding_detected ) {
                    padding_detected = c == 0x00;
                    crc_detected = 0;
                    padding_start_detected = 0;

                    if ( i == state_size * ( int ) sizeof( xnum_t ) - 1 ) {
                        read_block = 0;
                    }

                }

                if ( padding_start_detected ) {
                    padding_detected = c == 0x00;
                    crc_detected = 0;
                    padding_start_detected = 0;
                }

                if ( crc_detected ) {
                    crc_detected = 0;
                    padding_start_detected = c == 0x80;
                }

                if ( ( computed_crc[settings.crc_bytes] & crc_mask )
                     == ( read_shuffled_crc & crc_mask ) ) {
                    crc_detected = 1;
                }

                if ( ( crc_detected )
                     && ( !( padding_start_detected || padding_detected ) ) ) {

                    // restore the fifo except the character in the crc
                    if ( fwrite
                         ( c_fifo, sizeof( uint8_t ),
                           ( uint64_t ) fifo_index - ( uint64_t ) settings.crc_bytes,
                           fpo ) != ( ( uint64_t ) fifo_index - ( uint64_t ) settings.crc_bytes ) ) {

                        XRN_FREE_XSTATE( output_block_variable->xnums, settings.state_size );
                        XRN_FREEN( c_fifo, c_fifo_size );
                        XRN_FREE( output_block_variable );
                        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

                        XPRINT_ERR( 188, XERR_IO_MSG );
                        return XERR_IO;
                    }

                    // restore in the fifo the partial crc
                    for ( l = 0; l < settings.crc_bytes; l++ ) {
                        c_fifo[l] = c_fifo[fifo_index - settings.crc_bytes + l];
                    }

                    fifo_index = settings.crc_bytes;
                } else if ( ( fifo_index > settings.crc_bytes )
                            && ( !( padding_start_detected || padding_detected ) ) ) {

                    // restore the fifo except the character in the crc
                    if ( fwrite
                         ( c_fifo, sizeof( uint8_t ),
                           ( uint64_t ) fifo_index - ( uint64_t ) settings.crc_bytes,
                           fpo ) != ( fifo_index - ( int ) settings.crc_bytes ) ) {

                        XRN_FREE_XSTATE( output_block_variable->xnums, settings.state_size );
                        XRN_FREEN( c_fifo, c_fifo_size );
                        XRN_FREE( output_block_variable );
                        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

                        XPRINT_ERR( 189, XERR_IO_MSG );
                        return XERR_IO;
                    }

                    // restore in the fifo the partial crc
                    for ( l = 0; l < settings.crc_bytes; l++ ) {
                        c_fifo[l] = c_fifo[fifo_index - settings.crc_bytes + l];
                    }

                    fifo_index = settings.crc_bytes;
                }

            }

        }

    }

    if ( !( padding_start_detected || padding_detected ) ) {

        XRN_FREE_XSTATE( output_block_variable->xnums, settings.state_size );
        XRN_FREEN( c_fifo, c_fifo_size );
        XRN_FREE( output_block_variable );
        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

        XPRINT_ERR( 190, XERR_END_DEC_MSG );
        return XERR_END_DEC;
    }

    XRN_FREE_XSTATE( output_block_variable->xnums, settings.state_size );
    XRN_FREEN( c_fifo, c_fifo_size );
    XRN_FREE( output_block_variable );
    XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

    return final_ret;
}

int loop_join_bytes( uint8_t * in_bytes_challenge,
                     uint64_t in_length_challenge,
                     uint64_t in_index_challenge,
                     uint8_t * in_bytes_solution,
                     uint64_t in_length_solution,
                     uint64_t in_index_solution,
                     uint8_t * out_bytes,
                     uint64_t out_length,
                     uint64_t * num_decoded_bytes,
                     xrn_encoding_param_t enc_param_challenge, xrn_crypto_extra_settings_t settings
 )
{

    uint64_t block_i, c_fifo_size;
    uint64_t computed_crc[9], read_crc, read_shuffled_crc, crc_mask;
    uint8_t c, *c_fifo;
    uint8_t crc8;
    uint16_t crc16;
    uint32_t crc32;
    uint64_t crc64;
    int i, read_block1, fifo_index, first_loop;
    uint64_t index_challenge;
    int state_size, ret, l, final_ret;
    xrn_encoding_tmp_param_t enc_param_tmp;
    XBIT_TYPE padding_start_detected, padding_detected, crc_detected;
    xstate_t input_block_variable;
    xnum_t num0, num1, num2;

    if ( ( in_bytes_challenge == NULL ) || ( out_bytes == NULL )
         || ( num_decoded_bytes == NULL ) ) {
        XPRINT_ERR( 22, XERR_NULL_MSG );
        return XERR_NULL;
    }

    read_shuffled_crc = 0;
    final_ret = XSUCCESS;

    state_size = settings.state_size + 1;

    ( *num_decoded_bytes ) = 0;
    index_challenge = in_index_challenge;

    c_fifo_size = sizeof( xnum_t ) * ( uint64_t ) state_size;
    c_fifo = ( uint8_t * ) malloc( ( uint64_t ) c_fifo_size );
    if ( c_fifo == NULL ) {
        XPRINT_ERR( 23, XERR_NULL_MSG );
        return XERR_NULL;
    }

    input_block_variable.xnums =
        ( xnum_t * ) malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );

    if ( input_block_variable.xnums == NULL ) {

        XRN_FREEN( c_fifo, c_fifo_size );
        XPRINT_ERR( 26, XERR_NULL_MSG );
        return XERR_NULL;
    }

    for ( l = 0; l < 9; l++ ) {
        computed_crc[l] = 0xffffffffffffffff;
    }

    read_crc = 0x0000000000000000;
    if ( settings.crc_bytes == 1 ) {
        crc_mask = 0x00000000000000ff;
    } else if ( settings.crc_bytes == 2 ) {
        crc_mask = 0x000000000000ffff;
    } else if ( settings.crc_bytes == 4 ) {
        crc_mask = 0x00000000ffffffff;
    } else if ( settings.crc_bytes == 8 ) {
        crc_mask = 0xffffffffffffffff;
    } else {

        XRN_FREEN( c_fifo, c_fifo_size );
        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

        XPRINT_ERR( 27, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    crc_detected = 0;
    padding_start_detected = 0;
    padding_detected = 0;
    fifo_index = 0;
    read_block1 = 1;
    first_loop = 1;

    // Decrypting blocks
    for ( block_i = 0; read_block1 == 1; block_i++ ) {
        if ( first_loop == 1 ) {
            // reading challenge

            if ( index_challenge + sizeof( xnum_t ) * ( uint64_t ) ( state_size - 1 ) > in_length_challenge ) {

                XRN_FREEN( c_fifo, c_fifo_size );
                XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                XPRINT_ERR( 1188, XERR_BUFF_MSG );
                return XERR_BUFF;
            }

            for ( i = 0; i < ( sizeof( xnum_t ) * ( uint64_t ) ( state_size - 1 ) ); i++ ) {
                ( ( uint8_t * ) ( input_block_variable.xnums ) )[i] = in_bytes_challenge[index_challenge];
                index_challenge++;
            }

            // reading solution
            if ( in_index_solution + sizeof( xnum_t ) > in_length_solution ) {

                XRN_FREEN( c_fifo, c_fifo_size );
                XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                XPRINT_ERR( 1199, XERR_BUFF_MSG );
                return XERR_BUFF;
            }

            for ( i = 0; i < sizeof( xnum_t ); i++ ) {
                ( ( uint8_t * ) & num2 )[i] = in_bytes_solution[in_index_solution];
                in_index_solution++;
            }

            // get the first number
            // num2 = A xor B xor C
            // num1 = B xor C
            num1 = input_block_variable.xnums[1];
            // num0 = A
            X_XOR3( num2, num1, num0 );

            for ( i = 0; i < state_size - 1; i++ ) {
                num1 = input_block_variable.xnums[i];
                X_XOR3( num0, num1, num2 );
                input_block_variable.xnums[i] = num0;
                num0 = num2;
            }

            input_block_variable.xnums[state_size - 1] = num0;

        } else {

            // reading challenge

            if ( index_challenge + sizeof( xnum_t ) * ( uint64_t ) state_size > in_length_challenge ) {

                XRN_FREEN( c_fifo, c_fifo_size );
                XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                XPRINT_ERR( 251, XERR_BUFF_MSG );
                return XERR_BUFF;
            }

            for ( i = 0; i < ( sizeof( xnum_t ) * ( uint64_t ) state_size ); i++ ) {
                ( ( uint8_t * ) ( input_block_variable.xnums ) )[i] = in_bytes_challenge[index_challenge];
                index_challenge++;
            }

            // num0 is initialized by the previous loop
            for ( i = 0; i < state_size; i++ ) {
                num1 = input_block_variable.xnums[i];
                X_XOR3( num0, num1, num2 );
                input_block_variable.xnums[i] = num2;
                num0 = num2;
            }

        }

        // decode challenge

        ret =
            xrn_decode( &enc_param_challenge, ( uint8_t * ) input_block_variable.xnums,
                        ( uint8_t * ) input_block_variable.xnums,
                        sizeof( xnum_t ) * ( ( uint64_t ) state_size ), &enc_param_tmp );

        final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
        if ( final_ret == XERR_GENERAL ) {
            XRN_FREEN( c_fifo, c_fifo_size );
            XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
            XPRINT_ERR( 33, XERR_GENERAL_MSG );
            return XERR_GENERAL;
        }

        read_block1 =
            ( index_challenge + ( sizeof( xnum_t ) * ( uint64_t ) state_size ) ) <= in_length_challenge;

        // Calculate crc
        for ( i = 0; i < state_size * ( int ) sizeof( xnum_t ); i++ ) {
            c = ( ( uint8_t * ) & input_block_variable.xnums->xnum )[i];

            // fifo of calculated crc
            for ( l = settings.crc_bytes - 1; l >= 0; l-- ) {
                computed_crc[l + 1] = computed_crc[l];
            }

            if ( settings.crc_bytes == 1 ) {

                crc8 = ( uint8_t ) computed_crc[0] & 0x00000000000000ff;
                xrn_crc8( &c, 1, &crc8 );
                computed_crc[0] = ( uint64_t ) crc8 & crc_mask;

                read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );

                read_shuffled_crc = read_crc;
            } else if ( settings.crc_bytes == 2 ) {

                crc16 = ( uint16_t ) ( computed_crc[0] & crc_mask );
                xrn_crc16( &c, 1, &crc16 );
                computed_crc[0] = ( uint64_t ) crc16 & crc_mask;

                read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );

                read_shuffled_crc =
                    ( ( read_crc << 8 ) & 0x000000000000ff00 ) | ( ( read_crc >> 8 ) & 0x00000000000000ff );
            } else if ( settings.crc_bytes == 4 ) {

                crc32 = ( uint32_t ) computed_crc[0] & 0x00000000ffffffff;
                xrn_crc32( &c, 1, &crc32 );
                computed_crc[0] = ( uint64_t ) crc32 & 0x00000000ffffffff;

                read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );

                read_shuffled_crc =
                    ( ( read_crc << 24 ) & 0x00000000ff000000 ) |
                    ( ( read_crc << 8 ) & 0x0000000000ff0000 ) |
                    ( ( read_crc >> 8 ) & 0x000000000000ff00 ) | ( ( read_crc >> 24 ) & 0x00000000000000ff );
            } else if ( settings.crc_bytes == 8 ) {

                crc64 = ( uint64_t ) computed_crc[0] & crc_mask;
                xrn_crc64( &c, 1, &crc64 );
                computed_crc[0] = ( uint64_t ) crc64 & crc_mask;

                read_crc = ( ( read_crc << 8 ) | ( ( uint64_t ) c ) );

                read_shuffled_crc =
                    ( ( read_crc << 56 ) & 0xff00000000000000 ) |
                    ( ( read_crc << 40 ) & 0x00ff000000000000 ) |
                    ( ( read_crc << 24 ) & 0x0000ff0000000000 ) |
                    ( ( read_crc << 8 ) & 0x000000ff00000000 ) |
                    ( ( read_crc >> 8 ) & 0x00000000ff000000 ) |
                    ( ( read_crc >> 24 ) & 0x0000000000ff0000 ) |
                    ( ( read_crc >> 40 ) & 0x000000000000ff00 ) | ( ( read_crc >> 56 ) & 0x00000000000000ff );
            }

            c_fifo[fifo_index] = c;
            fifo_index++;

            if ( padding_detected ) {
                padding_detected = c == 0x00;
                crc_detected = 0;
                padding_start_detected = 0;
            }

            if ( padding_start_detected ) {
                padding_detected = c == 0x00;
                crc_detected = 0;
                padding_start_detected = 0;
            }

            if ( crc_detected ) {
                crc_detected = 0;
                padding_start_detected = c == 0x80;
            }

            if ( ( computed_crc[settings.crc_bytes] & crc_mask ) == ( read_shuffled_crc & crc_mask ) ) {
                crc_detected = 1;
            }

            if ( ( crc_detected ) && ( !( padding_start_detected || padding_detected ) ) ) {

                if ( out_length >=
                     ( *num_decoded_bytes ) + ( uint64_t ) fifo_index - ( uint64_t ) settings.crc_bytes ) {
                    for ( l = 0; l < fifo_index - settings.crc_bytes; l++ ) {
                        out_bytes[( *num_decoded_bytes )] = c_fifo[l];
                        ( *num_decoded_bytes )++;
                    }

                } else {
                    XRN_FREEN( c_fifo, c_fifo_size );
                    XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                    XPRINT_ERR( 35, XERR_IO_MSG );
                    return XERR_IO;
                }

                // restore in the fifo the partial crc
                for ( l = 0; l < settings.crc_bytes; l++ ) {
                    c_fifo[l] = c_fifo[fifo_index - settings.crc_bytes + l];
                }

                fifo_index = settings.crc_bytes;
            } else if ( ( fifo_index > settings.crc_bytes )
                        && ( !( padding_start_detected || padding_detected ) ) ) {

                // restore the fifo except the character in the crc
                if ( out_length >=
                     ( *num_decoded_bytes ) + ( uint64_t ) fifo_index - ( uint64_t ) settings.crc_bytes ) {
                    for ( l = 0; l < fifo_index - settings.crc_bytes; l++ ) {
                        out_bytes[( *num_decoded_bytes )] = c_fifo[l];
                        ( *num_decoded_bytes )++;
                    }

                } else {
                    XRN_FREEN( c_fifo, c_fifo_size );
                    XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
                    XPRINT_ERR( 36, XERR_IO_MSG );
                    return XERR_IO;
                }

                // restore in the fifo the partial crc
                for ( l = 0; l < settings.crc_bytes; l++ ) {
                    c_fifo[l] = c_fifo[fifo_index - settings.crc_bytes + l];
                }

                fifo_index = settings.crc_bytes;
            }

        }

        first_loop = 0;

    }

    if ( !( padding_start_detected || padding_detected ) ) {
        XRN_FREEN( c_fifo, c_fifo_size );
        XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );
        XPRINT_ERR( 37, XERR_END_DEC_MSG );
        return XERR_END_DEC;
    }

    XRN_FREEN( c_fifo, c_fifo_size );
    XRN_FREE_XSTATE( input_block_variable.xnums, settings.state_size );

    return XSUCCESS;

}

// the function adds a padding in a block_cipher_variable
int add_padding_block( xstate_t * bv, uint8_t padding_type, uint64_t start, uint64_t stop
 )
{
    int i;

    if ( bv == NULL ) {
        XPRINT_ERR( 191, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( start > stop ) {
        XPRINT_ERR( 192, XERR_PADDING_MSG );
        return XERR_PADDING;
    }

    switch ( padding_type ) {

            // padding 10000000  00000....... 
        case 1:

            // add 100...0
            ( ( uint8_t * ) ( bv->xnums ) )[start] = 0x80;
            // add 000...0
            for ( i = ( int ) start + 1; i < stop; i++ ) {

                ( ( uint8_t * ) ( bv->xnums ) )[i] = 0x00;
            }

            break;
        default:
            break;
    }

    return XSUCCESS;
}

// the function adds a padding in a cipher_variable
int add_padding_stream( xstate_t * bv, uint8_t padding_type, uint64_t start, uint64_t stop
 )
{
    int i;

    if ( bv == NULL ) {
        XPRINT_ERR( 193, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( start > stop ) {
        XPRINT_ERR( 194, XERR_PADDING_MSG );
        return XERR_PADDING;
    }

    switch ( padding_type ) {

            // padding 10000000  00000.......
        case 1:

            // add 100...0
            ( ( uint8_t * ) ( bv->xnums ) )[start] = 0x80;
            // add 000...0
            for ( i = ( int ) start + 1; i < stop; i++ ) {

                ( ( uint8_t * ) ( bv->xnums ) )[i] = 0x00;
            }

            break;
        default:
            break;
    }

    return XSUCCESS;
}

// the function calculates and returns the start of the padding
int padding_start_index( xstate_t * block_variable, xrn_crypto_extra_settings_t settings
 )
{
    int i;
    uint8_t byte;

    if ( block_variable == NULL ) {
        XPRINT_ERR( 195, XERR_NULL_MSG );
        return XERR_NULL;
    }

    i = ( int ) sizeof( xnum_t ) * ( ( int ) settings.state_size + 1 ) - 1;
    byte = ( ( uint8_t * ) ( block_variable->xnums ) )[i];
    while ( byte != 128 ) {
        i--;
        byte = ( ( uint8_t * ) ( block_variable->xnums ) )[i];

        if ( i < 0 ) {
            XPRINT_ERR( 196, XERR_PADDING_MSG );
            return XERR_PADDING;
        }

    }

    return i;
}

// this function add a checksum integrity check
int dump_two_xnums( xnum_t num1,
                    xnum_t num2,
                    xrn_lib_xtype_t type, uint8_t crc_bytes, FILE * fp, xrn_crypto_extra_settings_t settings
 )
{

    uint32_t crc32_local;
    xrn_encoding_param_t enc_param;
    int ret, final_ret;

    final_ret = XSUCCESS;

    ret = xrn_dump_header_file( fp, type, crc_bytes, settings.state_size, &enc_param, settings );

    final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
    if ( final_ret == XERR_GENERAL ) {
        XPRINT_ERR( 198, XERR_GENERAL_MSG );
        return ret;
    }

    if ( 1 != xrn_fwrite_encoder( &num1, sizeof( num1 ), 1, fp, &enc_param ) ) {
        XPRINT_ERR( 201, XERR_IO_MSG );
        return XERR_IO;
    }

    if ( 1 != xrn_fwrite_encoder( &num2, sizeof( num2 ), 1, fp, &enc_param ) ) {
        XPRINT_ERR( 202, XERR_IO_MSG );
        return XERR_IO;
    }

    crc32_local = 0xffffffff;
    xrn_crc32( ( uint8_t * ) & num1, sizeof( num1 ), &crc32_local );
    xrn_crc32( ( uint8_t * ) & num2, sizeof( num2 ), &crc32_local );

    if ( 1 != xrn_fwrite_encoder( &crc32_local, sizeof( crc32_local ), 1, fp, &enc_param ) ) {
        XPRINT_ERR( 203, XERR_IO_MSG );
        return XERR_IO;
    }

    return final_ret;

}

// this function performs a checksum integrity check
int load_two_xnums( xnum_t * xnum1,
                    xnum_t * xnum2,
                    uint8_t * crc_bytes,
                    uint8_t * state_size,
                    xrn_lib_xtype_t * type, xrn_lib_xtype_t exp_type, FILE * fp,
                    xrn_crypto_extra_settings_t settings
 )
{

    xnum_t xnum1_local;
    xnum_t xnum2_local;
    uint32_t crc32_local;
    uint32_t crc32_stored;
    uint32_t reserved_0, reserved_1;
    int ret, final_ret;
    xrn_encoding_param_t enc_param;

    if ( ( fp == NULL ) || ( xnum1 == NULL ) || ( xnum2 == NULL )
         || ( type == NULL ) || ( crc_bytes == NULL ) ) {
        XPRINT_ERR( 204, XERR_NULL_MSG );
        return XERR_NULL;
    }

    final_ret = XSUCCESS;

    ret = xrn_load_header_file( fp, crc_bytes, type, exp_type, state_size,
                                &reserved_0, &reserved_1, &enc_param, settings );

    final_ret = xrn_store_warnings_in_return_value( ret, final_ret );
    if ( final_ret == XERR_GENERAL ) {
        XPRINT_ERR( 205, XERR_GENERAL_MSG );
        return ret;
    }

    if ( xrn_fread_decoder( &xnum1_local, sizeof( xnum1_local ), 1, fp, &enc_param ) != 1 ) {
        XPRINT_ERR( 212, XERR_IO_MSG );
        return XERR_IO;
    }

    crc32_local = 0xffffffff;
    xrn_crc32( ( uint8_t * ) & xnum1_local, sizeof( xnum1_local ), &crc32_local );

    if ( xrn_fread_decoder( &xnum2_local, sizeof( xnum2_local ), 1, fp, &enc_param ) != 1 ) {
        XPRINT_ERR( 213, XERR_IO_MSG );
        return XERR_IO;
    }

    xrn_crc32( ( uint8_t * ) & xnum2_local, sizeof( xnum2_local ), &crc32_local );

    if ( xrn_fread_decoder( &crc32_stored, sizeof( crc32_stored ), 1, fp, &enc_param ) != 1 ) {
        XPRINT_ERR( 214, XERR_IO_MSG );
        return XERR_IO;
    }

    if ( crc32_stored != crc32_local ) {
        XPRINT_ERR( 215, XERR_CRC_MSG );
        return XERR_CRC;
    }

    *xnum1 = xnum1_local;
    *xnum2 = xnum2_local;

    return final_ret;

}

int xrn_mix( xstate_t * state_i,
             xrn_matrix_t * xm,
             xstate_t * hidden_state, xstate_t * state_o, xrn_crypto_extra_settings_t settings
 )
{
    xstate_t state;
    xnum_t tmp0;
    xnum_t tmp1;
    int i;
    int state_size;

    if ( ( state_i == NULL ) || ( xm == NULL ) || ( hidden_state == NULL ) || ( state_o == NULL ) ) {
        XPRINT_ERR( 216, XERR_NULL_MSG );
        return XERR_NULL;
    }

    state_size = settings.state_size + 1;
    state.xnums = ( xnum_t * ) malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );

    if ( state.xnums == NULL ) {
        XPRINT_ERR( 217, XERR_NULL_MSG );
        return XERR_NULL;
    }

    for ( i = 0; i < state_size; i++ ) {
        tmp0 = state_i->xnums[i];
        tmp1 = hidden_state->xnums[i];
        X_XOR3( tmp0, tmp1, tmp0 );
        state.xnums[i] = tmp0;
    }

    for ( i = 0; i < state_size; i++ ) {
        state_o->xnums[i] = state.xnums[i];
    }

    XRN_FREE_XSTATE( state.xnums, settings.state_size );
    return XSUCCESS;
}

int xrn_cross_traverses( xstate_t * state_i, xrn_matrix_t * xm, xstate_t * state_o,
                         xrn_crypto_extra_settings_t settings
 )
{
    int i;
    xstate_t state_tmp;
    int state_size;
    uint8_t constant_execution, force_linearity, check_sparcity;
    cmtraverse_t *arg;
    int *ret_thread;

#if XRN_GNU_LINUX == 1
    pthread_t *threads;
#else
    HANDLE *threads;
    unsigned *threadID;
#endif

    force_linearity = 0;
    check_sparcity = 0;
    constant_execution = settings.const_exec_time;

    if ( ( state_i == NULL ) || ( xm == NULL ) || ( state_o == NULL ) ) {
        XPRINT_ERR( 218, XERR_NULL_MSG );
        return XERR_NULL;
    }

    state_size = settings.state_size + 1;
    if ( ( settings.state_size < 1 ) || ( settings.state_size > ( sizeof( xnum_t ) * 8 ) - 1 ) ) {
        XPRINT_ERR( 219, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    state_tmp.xnums = ( xnum_t * ) malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( state_tmp.xnums == NULL ) {
        XPRINT_ERR( 220, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( settings.thread_enable == 0 ) {
        for ( i = 0; i < state_size; i++ ) {

            xrn_matrix_traverse( xm, &state_i->xnums[i], &state_i->xnums[( i + 1 ) % state_size],
                                 &state_tmp.xnums[i], &constant_execution, &force_linearity,
                                 &check_sparcity );
        }

    } else {

        ret_thread = ( int * ) malloc( sizeof( int ) * ( uint64_t ) state_size );
        if ( ret_thread == NULL ) {
            XRN_FREE_XSTATE( state_tmp.xnums, settings.state_size );
            XPRINT_ERR( 221, XERR_NULL_MSG );
            return XERR_NULL;
        }

        arg = ( cmtraverse_t * ) malloc( ( uint64_t ) sizeof( cmtraverse_t ) * ( uint64_t ) state_size );
        if ( arg == NULL ) {
            XRN_FREEN( ret_thread, ( sizeof( int ) * ( ( uint64_t ) state_size ) ) );
            XRN_FREE_XSTATE( state_tmp.xnums, settings.state_size );
            XPRINT_ERR( 222, XERR_NULL_MSG );
            return XERR_NULL;
        }

#if XRN_GNU_LINUX == 1
        threads = ( pthread_t * ) malloc( ( uint64_t ) sizeof( pthread_t ) * ( uint64_t ) state_size );
        if ( threads == NULL ) {
            XRN_FREEN( ret_thread, ( sizeof( int ) * ( ( uint64_t ) state_size ) ) );
            XRN_FREE_XSTATE( state_tmp.xnums, settings.state_size );
            XRN_FREEN( arg, ( sizeof( cmtraverse_t ) * ( ( uint64_t ) state_size ) ) );
            XPRINT_ERR( 223, XERR_NULL_MSG );
            return XERR_NULL;
        }

#else
        threads = ( HANDLE * ) malloc( ( uint64_t ) sizeof( HANDLE ) * ( uint64_t ) state_size );
        if ( threads == NULL ) {
            XRN_FREEN( ret_thread, ( sizeof( int ) * ( ( uint64_t ) state_size ) ) );
            XRN_FREE_XSTATE( state_tmp.xnums, settings.state_size );
            XRN_FREEN( arg, ( sizeof( cmtraverse_t ) * ( ( uint64_t ) state_size ) ) );
            XPRINT_ERR( 224, XERR_NULL_MSG );
            return XERR_NULL;
        }

        threadID = ( unsigned * ) malloc( ( uint64_t ) sizeof( unsigned ) * ( uint64_t ) state_size );
        if ( threadID == NULL ) {
            XRN_FREE( ret_thread );
            XRN_FREE_XSTATE( state_tmp.xnums, settings.state_size );
            XRN_FREE( arg );
            XPRINT_ERR( 225, XERR_NULL_MSG );
            return XERR_NULL;
        }

#endif

        for ( i = 0; i < state_size; i++ ) {
            arg[i].xm = xm;
            arg[i].p = &state_i->xnums[i];
            arg[i].d = &state_i->xnums[( i + 1 ) % state_size];
            arg[i].dst = &state_tmp.xnums[i];
            arg[i].constant_execution = &constant_execution;
            arg[i].force_linearity = &force_linearity;
            arg[i].check_sparcity = &check_sparcity;
            arg[i].linear = &ret_thread[i];

#if XRN_GNU_LINUX == 1
            if ( 0 != pthread_create( &threads[i], NULL, xrn_matrix_traverse_pthread, ( void * )
                                      &arg[i] ) ) {
                XRN_FREEN( ret_thread, ( sizeof( int ) * ( ( uint64_t ) state_size ) ) );
                XRN_FREE_XSTATE( state_tmp.xnums, settings.state_size );
                XRN_FREEN( arg, ( sizeof( cmtraverse_t ) * ( ( uint64_t ) state_size ) ) );
                XRN_FREEN( threads, ( sizeof( pthread_t ) * ( ( uint64_t ) state_size ) ) );

                XPRINT_ERR( 226, XERR_NULL_MSG );
                return XERR_NULL;
            }

#else
            if ( ( threads[i] =
                   ( HANDLE ) _beginthreadex( NULL, 0,
                                              ( unsigned int ( * )( void * ) ) xrn_matrix_traverse_pthread,
                                              ( void * ) &arg[i], 0, &threadID[i] ) ) == 0 ) {

                XRN_FREEN( ret_thread, ( sizeof( int ) * ( ( uint64_t ) state_size ) ) );
                XRN_FREE_XSTATE( state_tmp.xnums, settings.state_size );
                XRN_FREEN( arg, ( sizeof( cmtraverse_t ) * ( ( uint64_t ) state_size ) ) );
                XRN_FREEN( threads, ( sizeof( pthread_t ) * ( ( uint64_t ) state_size ) ) );
                XRN_FREEN( threadID, ( sizeof( unsigned ) * ( ( uint64_t ) state_size ) ) );
                XPRINT_ERR( 227, XERR_NULL_MSG );
                return XERR_NULL;
            }

#endif

        }

        for ( i = 0; i < state_size; i++ ) {

#if XRN_GNU_LINUX == 1
            pthread_join( threads[i], NULL );
#else
            WaitForSingleObject( threads[i], INFINITE );
            for ( i = 0; i < state_size; i++ ) {
                CloseHandle( threads[i] );
            }

            XRN_FREEN( threadID, ( sizeof( unsigned ) * ( ( uint64_t ) state_size ) ) );
#endif
        }

        XRN_FREEN( ret_thread, ( sizeof( int ) * ( ( uint64_t ) state_size ) ) );
        XRN_FREEN( arg, ( sizeof( cmtraverse_t ) * ( ( uint64_t ) state_size ) ) );
        XRN_FREEN( threads, ( sizeof( pthread_t ) * ( ( uint64_t ) state_size ) ) );

    }

    for ( i = 0; i < state_size; i++ ) {
        state_o->xnums[i] = state_tmp.xnums[i];
    }

    XRN_FREE_XSTATE( state_tmp.xnums, settings.state_size );

    return XSUCCESS;
}

// this function compresses a state into a xnum
void xrn_compress_state( xstate_t state, xnum_t * o, xrn_crypto_extra_settings_t settings
 )
{
    xnum_t num;
    int i;
    int state_size;

    state_size = settings.state_size + 1;

    num = state.xnums[0];
    for ( i = 1; i < state_size; i++ ) {
        X_XOR3( state.xnums[i], num, num );
    }

    *o = num;
    return;
}

int xrn_round_state( xstate_t * state_i,
                     xrn_matrix_t * xm,
                     xstate_t * hidden_state, xstate_t * state_o, xrn_crypto_extra_settings_t settings
 )
{
    xstate_t state;
    int i;
    int state_size;

    if ( ( state_i == NULL ) || ( xm == NULL ) || ( hidden_state == NULL ) || ( state_o == NULL ) ) {
        XPRINT_ERR( 228, XERR_NULL_MSG );
        return XERR_NULL;
    }

    state_size = settings.state_size + 1;
    state.xnums = ( xnum_t * )
        malloc( ( uint64_t ) sizeof( xnum_t ) * ( uint64_t ) state_size );
    if ( state.xnums == NULL ) {
        XPRINT_ERR( 229, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( XSUCCESS != xrn_mix( state_i, xm, hidden_state, &state, settings ) ) {
        XRN_FREE_XSTATE( state.xnums, settings.state_size );
        XPRINT_ERR( 230, XERR_CRYPTO_MSG );
        return XERR_CRYPTO;
    }

    if ( XSUCCESS != xrn_cross_traverses( &state, xm, &state, settings ) ) {
        XRN_FREE_XSTATE( state.xnums, settings.state_size );
        XPRINT_ERR( 231, XERR_CRYPTO_MSG );
        return XERR_CRYPTO;
    }

    for ( i = 0; i < state_size; i++ ) {
        state_o->xnums[i] = state.xnums[i];
    }

    XRN_FREE_XSTATE( state.xnums, settings.state_size );
    return XSUCCESS;
}

// initialization of block_load_variables_t before reading the file 
void crypto_block_init( block_load_variables_t * load_variables, xrn_crypto_extra_settings_t settings
 )
{
    load_variables->extra_block_needed = 0;
}

// initialization of hash_load_variables_t before reading the file 
void crypto_hash_init( hash_load_variables_t * load_variables
 )
{
    load_variables->extra_block_needed = 0;
    load_variables->last_block = 0;
}

// initialization of load_variables_t before reading the file 
void crypto_init( load_variables_t * load_variables, uint8_t crc_bytes
 )
{
    load_variables->extra_block_needed = 0;
    load_variables->crc = 0xffffffffffffffff;
    load_variables->written_crc_bytes = 0;
    load_variables->last_block = 0;
}

int shuffle_binomial_keys( xring_signature_t * sig
 )
{

    int i;
    uint8_t rnd;
    xbinomial_key_t tmp;

    if ( sig == NULL ) {
        XPRINT_ERR( 232, XERR_NULL_MSG );
        return XERR_NULL;
    }

    // shuffle
    for ( i = sig->ring_size - 1; i >= 0; i-- ) {
        if ( get_random_bytes( &rnd, 1 ) != XSUCCESS ) {
            XPRINT_ERR( 233, XERR_IO_MSG );
            return XERR_IO;
        }

        rnd = rnd % ( uint8_t ) ( i + 1 );
        tmp = sig->binomial_keys[i];
        sig->binomial_keys[i] = sig->binomial_keys[rnd];
        sig->binomial_keys[rnd] = tmp;
    }

    return XSUCCESS;
}

int expand_xnum( xrn_matrix_t * xm, xnum_t iv, xstate_t * state_o, xrn_crypto_extra_settings_t settings
 )
{
    int i;
    int state_size;
    traverse_opt_t trav_tmp;

    if ( ( xm == NULL ) || ( state_o == NULL ) ) {
        XPRINT_ERR( 234, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( XSUCCESS != check_crypto_extra_settings_value( settings ) ) {
        XPRINT_ERR( 235, XERR_SETTINGS_MSG );
        return XERR_SETTINGS;
    }

    state_size = settings.state_size + 1;

    // initialize state
    for ( i = 0; i < state_size; i++ ) {
        xrn_vector_traverse( &( xm->xvector[i] ), &xm->xrn_dead_code, &iv, &iv, &trav_tmp );
        state_o->xnums[i] = iv;
    }

    return XSUCCESS;
}

int init_round_state( xrn_matrix_t * xm,
                      xnum_t init, uint64_t block_id, xstate_t * state, xrn_crypto_extra_settings_t settings
 )
{
    int i, ret;
    xnum_t tmp;

    if ( ( xm == NULL ) || ( state == NULL ) ) {
        XPRINT_ERR( 236, XERR_NULL_MSG );
        return XERR_NULL;
    }

    // concatenate init and block_id
    for ( i = 0; i < sizeof( xnum_t ) - sizeof( uint64_t ); i++ ) {
        ( ( uint8_t * ) & tmp )[i] = ( ( uint8_t * ) & init )[i];
    }

    for ( i = 0; i < sizeof( uint64_t ); i++ ) {
        ( ( uint8_t * ) & tmp )[sizeof( xnum_t ) - sizeof( uint64_t ) + ( uint64_t ) i] =
            ( ( uint8_t * ) & block_id )[i];
    }

    ret = expand_xnum( xm, tmp, state, settings );
    if ( XSUCCESS != ret ) {
        XPRINT_ERR( 237, XERR_GENERAL_MSG );
        return ret;
    }

    return XSUCCESS;
}

int plain_text_2_cipher( FILE * fpi,
                         int index,
                         xstate_t * bv,
                         load_variables_t * load_variables, xrn_crypto_extra_settings_t settings
 )
{
    int32_t byte_read;
    uint64_t mask;
    int shift;
    uint64_t crc64;
    uint32_t crc32;
    uint16_t crc16;
    uint8_t crc8;
    int input_bytes_in_block, i;
    int state_size;

    if ( ( fpi == NULL ) || ( bv == NULL ) || ( load_variables == NULL ) ) {
        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 252, XERR_NULL_MSG );
        return XERR_NULL;
    }

    state_size = settings.state_size + 1;
    input_bytes_in_block = ( int ) ( ( uint64_t ) state_size * sizeof( xnum_t ) );

    if ( load_variables->extra_block_needed == 1 ) {
        load_variables->crc = load_variables->crc >> ( 8 * load_variables->written_crc_bytes );
        for ( i = 0; i < settings.crc_bytes - load_variables->written_crc_bytes; i++ ) {
            ( ( uint8_t * ) ( bv->xnums ) )[i] = ( uint8_t ) ( load_variables->crc & ( 0x00000000000000ff ) );
            load_variables->crc = load_variables->crc >> 8;
        }

        if ( add_padding_stream
             ( bv, 1,
               ( uint64_t ) settings.crc_bytes - ( uint64_t ) load_variables->written_crc_bytes,
               ( uint64_t ) input_bytes_in_block ) != 0 ) {
            XPRINT_ERR( 254, XERR_PADDING_MSG );
            return XERR_PADDING;
        }

        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        return XSUCCESS;
    }

    if ( fseek( fpi, ( ( int ) index * ( int ) input_bytes_in_block ), 0 ) != 0 ) {
        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 255, XERR_IO_MSG );
        return XERR_IO;
    }

    // read the file
    byte_read = ( int32_t ) fread( bv->xnums, sizeof( uint8_t ), ( uint64_t ) input_bytes_in_block, fpi );
    if ( byte_read == 0 ) {
        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 256, XERR_IO_MSG );
        return XERR_IO;
    }

    // compute the crc
    if ( settings.crc_bytes == 1 ) {

        crc8 = ( uint8_t ) ( load_variables->crc & 0x00000000000000ff );
        xrn_crc8( ( uint8_t * ) bv->xnums, byte_read, &crc8 );
        load_variables->crc = ( ( uint64_t ) crc8 ) & 0x00000000000000ff;

    } else if ( settings.crc_bytes == 2 ) {

        crc16 = ( uint16_t ) ( load_variables->crc & 0x000000000000ffff );
        xrn_crc16( ( uint8_t * ) bv->xnums, byte_read, &crc16 );
        load_variables->crc = ( ( uint64_t ) crc16 ) & 0x000000000000ffff;

    } else if ( settings.crc_bytes == 4 ) {

        crc32 = ( uint32_t ) ( load_variables->crc & 0x00000000ffffffff );
        xrn_crc32( ( uint8_t * ) bv->xnums, byte_read, &crc32 );
        load_variables->crc = ( ( uint64_t ) crc32 ) & 0x00000000ffffffff;

    } else if ( settings.crc_bytes == 8 ) {

        crc64 = ( uint64_t ) ( load_variables->crc & 0xffffffffffffffff );
        xrn_crc64( ( uint8_t * ) bv->xnums, byte_read, &crc64 );
        load_variables->crc = ( ( uint64_t ) crc64 ) & 0xffffffffffffffff;

    }

    // last block large enough to put padding and crc
    if ( byte_read < input_bytes_in_block - settings.crc_bytes ) {
        load_variables->extra_block_needed = 0;
        load_variables->last_block = 1;
        // write the crc
        for ( i = 0; i < settings.crc_bytes; i++ ) {
            ( ( uint8_t * ) ( bv->xnums ) )
                [byte_read + i] = ( uint8_t ) ( load_variables->crc & 0x00000000000000ff );
            load_variables->crc = load_variables->crc >> 8;
        }

        // write the padding
        if ( add_padding_stream
             ( bv, 1, ( uint64_t ) byte_read + settings.crc_bytes,
               ( uint64_t ) input_bytes_in_block ) != 0 ) {
            XPRINT_ERR( 257, XERR_PADDING_MSG );
            return XERR_PADDING;
        }

        // last block large enough to start putting the crc but extra block needed
    } else if ( ( input_bytes_in_block - byte_read <= settings.crc_bytes )
                && ( xrn_feof( fpi ) ) ) {
        load_variables->extra_block_needed = 1;
        load_variables->written_crc_bytes = ( uint8_t ) ( input_bytes_in_block - byte_read );
        load_variables->last_block = 0;

        mask = 0x00000000000000ff;
        shift = 0;
        // write the crc
        for ( i = 0; i < load_variables->written_crc_bytes; i++ ) {
            ( ( uint8_t * ) ( bv->xnums ) )
                [byte_read + i] = ( uint8_t ) ( ( ( uint64_t ) load_variables->crc & mask ) >> shift );
            shift = shift + 8;
            mask = mask << 8;

        }

        // not last block
    } else {
        load_variables->extra_block_needed = 0;
        load_variables->last_block = 0;
    }

    return XSUCCESS;
}

int plain_text_2_hash_block( FILE * fpi,
                             int index,
                             xstate_t * bv,
                             hash_load_variables_t * load_variables, xrn_crypto_extra_settings_t settings
 )
{
    int32_t byte_read;
    int input_bytes_in_block;
    int state_size;

    if ( ( fpi == NULL ) || ( bv == NULL ) || ( load_variables == NULL ) ) {
        XPRINT_ERR( 258, XERR_NULL_MSG );
        return XERR_NULL;
    }

    state_size = settings.state_size + 1;
    input_bytes_in_block = state_size * ( int ) sizeof( xnum_t );

    if ( load_variables->extra_block_needed == 1 ) {
        if ( XSUCCESS != add_padding_block( bv, 1, 0, ( uint64_t ) input_bytes_in_block ) ) {
            XPRINT_ERR( 260, XERR_PADDING_MSG );
            return XERR_PADDING;
        }

        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        return XSUCCESS;
    }

    // seeking the file pointer to the requested block location
    if ( fseek( fpi, ( index * input_bytes_in_block ), 0 ) != 0 ) {
        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 261, XERR_IO_MSG );
        return XERR_IO;
    }

    // read the file
    byte_read = ( int ) fread( bv->xnums, sizeof( uint8_t ), ( uint64_t ) input_bytes_in_block, fpi );
    if ( byte_read == 0 ) {
        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 262, XERR_IO_MSG );
        return XERR_IO;
    }

    // last block large enough to put padding and
    if ( byte_read < input_bytes_in_block ) {

        load_variables->extra_block_needed = 0;
        load_variables->last_block = 1;
        // write padding
        if ( XSUCCESS !=
             add_padding_block( bv, 1, ( uint64_t ) byte_read, ( uint64_t ) input_bytes_in_block ) ) {
            XPRINT_ERR( 263, XERR_PADDING_MSG );
            return XERR_PADDING;
        }

        // last block large enough to put what it is read 
    } else if ( ( byte_read == input_bytes_in_block ) && ( xrn_feof( fpi ) ) ) {
        load_variables->extra_block_needed = 1;
        load_variables->last_block = 0;
        // not last block
    } else {
        load_variables->extra_block_needed = 0;
        load_variables->last_block = 0;
    }

    return XSUCCESS;
}

int bytes_2_cipher( uint8_t * bytes,
                    int length,
                    int index,
                    xstate_t * bv, load_variables_t * load_variables, xrn_crypto_extra_settings_t settings
 )
{
    int32_t byte_read;
    uint64_t crc64;
    uint32_t crc32;
    uint16_t crc16;
    uint8_t crc8;
    int input_bytes_in_block, i;
    int start, read_index;
    int state_size;

    if ( ( bytes == NULL ) || ( bv == NULL ) || ( load_variables == NULL ) ) {
        XPRINT_ERR( 264, XERR_NULL_MSG );
        return XERR_NULL;
    }

    state_size = settings.state_size + 1;
    input_bytes_in_block = state_size * ( int ) sizeof( xnum_t );
    if ( bytes == NULL ) {
        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 265, XERR_NULL_MSG );
        return XERR_NULL;
    }

    // if the file size is exhactly divisible by (sizeof(xnum_t)*8) * settings.state_size  bits
    if ( load_variables->extra_block_needed == 1 ) {
        for ( i = 0; i < settings.crc_bytes - load_variables->written_crc_bytes; i++ ) {
            ( ( uint8_t * ) ( bv->xnums ) )[i] = ( uint8_t ) ( load_variables->crc & ( 0x00000000000000ff ) );
            load_variables->crc = load_variables->crc >> 8;
        }

        if ( add_padding_stream
             ( bv, 1,
               ( uint64_t ) settings.crc_bytes - ( uint64_t ) load_variables->written_crc_bytes,
               ( uint64_t ) input_bytes_in_block ) != 0 ) {
            XPRINT_ERR( 266, XERR_PADDING_MSG );
            return XERR_PADDING;
        }

        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        return XSUCCESS;
    }

    // computing start index location
    start = index * input_bytes_in_block;
    if ( start > length ) {
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 267, XERR_IO_MSG );
        return XERR_IO;
    }

    // copy string into block
    for ( read_index = start;
          ( read_index < length ) && ( read_index < ( start + input_bytes_in_block ) ); read_index++ ) {
        ( ( uint8_t * ) ( bv->xnums ) )[read_index - start] = bytes[read_index];        // padding present
    }

    byte_read = read_index - start;
    if ( byte_read == 0 ) {
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 268, XERR_IO_MSG );
        return XERR_IO;
    }

    // compute the crc
    if ( settings.crc_bytes == 1 ) {

        crc8 = ( uint8_t ) ( load_variables->crc & 0x00000000000000ff );
        xrn_crc8( ( uint8_t * ) bv->xnums, byte_read, &crc8 );
        load_variables->crc = ( ( uint64_t ) crc8 ) & 0x00000000000000ff;

    } else if ( settings.crc_bytes == 2 ) {
        crc16 = ( uint16_t ) ( load_variables->crc & 0x000000000000ffff );
        xrn_crc16( ( uint8_t * ) bv->xnums, byte_read, &crc16 );
        load_variables->crc = ( ( uint64_t ) crc16 ) & 0x000000000000ffff;

    } else if ( settings.crc_bytes == 4 ) {

        crc32 = ( uint32_t ) ( load_variables->crc & 0x00000000ffffffff );
        xrn_crc32( ( uint8_t * ) bv->xnums, byte_read, &crc32 );
        load_variables->crc = ( ( uint64_t ) crc32 ) & 0x00000000ffffffff;

    } else if ( settings.crc_bytes == 8 ) {

        crc64 = ( uint64_t ) ( load_variables->crc & 0xffffffffffffffff );
        xrn_crc64( ( uint8_t * ) bv->xnums, byte_read, &crc64 );
        load_variables->crc = ( ( uint64_t ) crc64 ) & 0xffffffffffffffff;

    }

    // last block large enough to put padding and crc
    if ( byte_read < input_bytes_in_block - settings.crc_bytes ) {
        load_variables->extra_block_needed = 0;
        load_variables->last_block = 1;
        // write the crc
        for ( i = byte_read; i < byte_read + settings.crc_bytes; i++ ) {
            ( ( uint8_t * ) ( bv->xnums ) )[i] = ( uint8_t ) ( load_variables->crc & ( 0x00000000000000ff ) );
            load_variables->crc = load_variables->crc >> 8;
        }

        // write the padding
        if ( add_padding_stream
             ( bv, 1, ( uint64_t ) byte_read + ( uint64_t ) settings.crc_bytes,
               ( uint64_t ) input_bytes_in_block ) != 0 ) {
            XPRINT_ERR( 269, XERR_PADDING_MSG );
            return XERR_PADDING;
        }

        // last block large enough to start putting the crc but extra block needed
    } else if ( ( input_bytes_in_block - byte_read <= settings.crc_bytes ) &&
                ( length == ( start + byte_read ) ) ) {
        load_variables->extra_block_needed = 1;
        load_variables->written_crc_bytes = ( uint8_t ) ( input_bytes_in_block - byte_read );
        load_variables->last_block = 0;
        // write the crc
        for ( i = byte_read; i < byte_read + load_variables->written_crc_bytes; i++ ) {
            ( ( uint8_t * ) ( bv->xnums ) )[i] = ( uint8_t ) ( load_variables->crc & ( 0x00000000000000ff ) );
            load_variables->crc = load_variables->crc >> 8;
        }

        // not last block
    } else {
        load_variables->extra_block_needed = 0;
        load_variables->last_block = 0;
    }

    return XSUCCESS;
}

int from_bytes_2_hash_block( uint8_t * bytes,
                             int len,
                             int index,
                             xstate_t * bv,
                             hash_load_variables_t * load_variables, xrn_crypto_extra_settings_t settings
 )
{
    int32_t byte_read;
    int input_bytes_in_block;
    int start, read_index;
    int state_size;

    if ( ( bytes == NULL ) || ( bv == NULL ) || ( load_variables == NULL ) ) {
        XPRINT_ERR( 270, XERR_NULL_MSG );
        return XERR_NULL;
    }

    state_size = settings.state_size + 1;
    input_bytes_in_block = state_size * ( int ) sizeof( xnum_t );
    if ( bytes == NULL ) {
        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 271, XERR_NULL_MSG );
        return XERR_NULL;
    }

    if ( load_variables->extra_block_needed == 1 ) {
        if ( XSUCCESS != add_padding_block( bv, 1, 0, ( uint64_t ) input_bytes_in_block ) ) {
            XPRINT_ERR( 272, XERR_PADDING_MSG );
            return XERR_PADDING;
        }

        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        return XSUCCESS;
    }

    // computing start index location
    start = index * input_bytes_in_block;
    if ( start > len ) {
        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 273, XERR_IO_MSG );
        return XERR_IO;
    }

    // copy bytes into block
    for ( read_index = start;
          ( read_index < len ) && ( read_index < ( start + input_bytes_in_block ) ); read_index++ ) {
        ( ( uint8_t * ) ( bv->xnums ) )
            [read_index - start] = bytes[read_index];
    }

    byte_read = read_index - start;
    if ( byte_read == 0 ) {
        load_variables->last_block = 1;
        load_variables->extra_block_needed = 0;
        XPRINT_ERR( 274, XERR_IO_MSG );
        return XERR_IO;
    }

    // last block large enough to put padding and
    if ( byte_read < input_bytes_in_block ) {
        load_variables->extra_block_needed = 0;
        load_variables->last_block = 1;
        // write the padding
        if ( XSUCCESS !=
             add_padding_block( bv, 1, ( uint64_t ) byte_read, ( uint64_t ) input_bytes_in_block ) ) {
            XPRINT_ERR( 275, XERR_PADDING_MSG );
            return XERR_PADDING;
        }

        // last block large enough to put what it is read 
    } else if ( ( input_bytes_in_block == byte_read )
                && ( read_index == len ) ) {
        load_variables->extra_block_needed = 1;
        load_variables->last_block = 0;
        // not last block
    } else {
        load_variables->extra_block_needed = 0;
        load_variables->last_block = 0;
    }

    return XSUCCESS;
}

//Printing encrypted and decrypted blocks
void print_block_variable( xstate_t * block_variable, int size
 )
{
    int i;

    for ( i = 0; i < size; i++ ) {
        printf( "%02x ", ( ( uint8_t * ) ( block_variable->xnums ) )[i] );
    }

    printf( "\n" );
    return;
}

void print_extra_settings_t( xrn_crypto_extra_settings_t settings
 )
{

    printf( "settings.rnd_str %s \n", settings.rnd_str );
    printf( "settings.crc_bytes %d \n", settings.crc_bytes );
    printf( "settings.state_size %d \n", settings.state_size );
    printf( "settings.thread_enable %d \n", settings.thread_enable );
    printf( "settings.const_exec_time %d \n", settings.const_exec_time );

    return;

}

void initialize_xstate_t( xstate_t * state, uint8_t state_size
 )
{
    int i;

    for ( i = 0; i < state_size + 1; i++ ) {
        xrn_initialize_num( &state->xnums[i] );
    }

    return;
}

void print_xblock_settings_t( xblock_settings_t block
 )
{

    printf( "start_block %ld\n", block.start_block );
    printf( "end_block %ld\n", block.end_block );
    printf( "all %d\n", block.all );

}

//Printing state numbers
void print_state_xnums( xstate_t state, int state_size
 )
{
    int i;

    char buff[256];

    for ( i = 0; i < state_size; i++ ) {
        xrn_bin_to_base64( state.xnums[i], buff );
        printf( "%s ", buff );

    }

}

